// Copyright 2009 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Author: Gunnar Ritter
//
// Settings for testing a Google Mail account.

#include "util/provider.h"

#include <QtCore/QStringList>

#include "util/common.h"
#include "ui/support.h"
#include "config/configverifier.h"
#include "probes/imapprobe.h"
#include "probes/popprobe.h"
#include "probes/smtpprobe.h"

namespace popdiag {

class GmailProvider : public Provider {
 private:
  QString GooglemailDomainName() const {
    return "googlemail.com";
  }

  QString GmailDomainName() const {
    return "gmail.com";
  }

  QString DomainName() {
   return BrandIsGooglemailHere() ? GooglemailDomainName() : GmailDomainName();
  }

  bool IsOurMailServer(const QString &hostname,
                       const QString &protocol) {
    return (hostname.compare(protocol + "." + GooglemailDomainName(),
                             Qt::CaseInsensitive) == 0 ||
            hostname.compare(protocol + "." + GmailDomainName(),
                             Qt::CaseInsensitive) == 0);
  }

  bool VerifyUsernameFromEmailAddress(const QString &username,
                                      const QString &email) {
    if (username.contains('@'))
      return username.compare(email, Qt::CaseInsensitive) == 0;
    QString email_prefix = QString(email).left(email.indexOf('@'));
    return username.compare(email_prefix, Qt::CaseInsensitive) == 0;
  }

 public:
  QString Name() {
    return "Gmail";
  }

  bool PredefinedServers() {
    return true;
  }

  bool NeedEmailAddress() {
    return true;
  }

  QString AccountValidationExpression() {
    return "^[a-zA-Z0-9-][a-zA-Z0-9-.]*@[a-zA-Z0-9-.]+$";
  }

  QString SampleEmailAddress() {
    return "bob@" + DomainName();
  }

  QString PopServer() {
    return "pop." + DomainName();
  }

  bool IsOurPopServer(const QString &hostname) {
    return IsOurMailServer(hostname, "pop");
  }

  bool PopMustNotLeaveOnServer() {
    return true;
  }

  bool PopVerifyUsernameFromEmailAddress(const QString &username,
                                         const QString &email) {
    return VerifyUsernameFromEmailAddress(username, email);
  }

  QString SmtpServer() {
    return "smtp." + DomainName();
  }

  bool IsOurSmtpServer(const QString &hostname) {
    return IsOurMailServer(hostname, "smtp");
  }

  bool SmtpVerifyUsernameFromEmailAddress(const QString &username,
                                          const QString &email) {
    return VerifyUsernameFromEmailAddress(username, email);
  }

  bool SmtpCanUsePlaintextPort() {
    return false;
  }

  QString ImapServer() {
    return "imap." + DomainName();
  }

  bool IsOurImapServer(const QString &hostname) {
    return IsOurMailServer(hostname, "imap");
  }

  bool ImapVerifyUsernameFromEmailAddress(const QString &username,
                                          const QString &email) {
    return VerifyUsernameFromEmailAddress(username, email);
  }

  bool HasSupportLinks() {
    return true;
  }

 private:
  struct faultmap {
    int id;
    const char *topic;
  };

  QString GetFaultLinkWithMap(int fault_code, const struct faultmap *faultmap) {
    const char *topic = "unknown";
    for (int i = 0; faultmap[i].topic != NULL; ++i) {
      if (faultmap[i].id == fault_code) {
        topic = faultmap[i].topic;
        break;
      }
    }
    return "http://mail.google.com/support/bin/static.py?page=redirect.cs&rd=" +
           QString(topic);
  }

  QString GetSupportLink(int fault_code) {
    const struct faultmap faultmap[] = {
      { Support::TOOL_PURPOSE, "tools_purpose" },
      { Support::TESTS_RUNNING, "tests_running" },
      { Support::NO_PROBE_FAULTS_FOUND, "no_probe_faults_found" },
      { Support::UNKNOWN, NULL }
    };
    return GetFaultLinkWithMap(fault_code, faultmap);
  }

 public:
  QString GetConfigFaultLink(int fault_code, QString /*client_id*/) {
    const struct faultmap faultmap[] = {
      { PROFILE_NOT_UNDERSTOOD, "profile_not_understood" },
      { POP3_SERVER, "pop3_server" },
      { POP3_PORT, "pop3_port" },
      { POP3_USE_SSL, "pop3_use_ssl" },
      { POP3_LEAVE_ON_SERVER, "pop3_leave_on_server" },
      { POP3_USE_SPA, "pop3_use_spa" },
      { POP3_USERNAME, "pop3_username" },
      { POP3_TIMEOUT, "pop3_timeout" },
      { POP3_ACCOUNT_SKIPPED, "pop3_account_skipped" },
      { SMTP_SERVER, "smtp_server" },
      { SMTP_PORT_465, "smtp_port_465" },
      { SMTP_PORT_587, "smtp_port_587" },
      { SMTP_USE_AUTH, "smtp_use_auth" },
      { SMTP_USE_SSL, "smtp_use_ssl" },
      { SMTP_USERNAME, "smtp_username" },
      { SMTP_USE_SPA, "smtp_use_spa" },
      { SMTP_SPLIT_MESSAGES, "smtp_split_messages" },
      { SMTP_TIMEOUT, "smtp_timeout" },
      { SMTP_EMAIL_ADDRESS, "smtp_email_address" },
      { SMTP_AUTH_POP_BEFORE_SMTP, "smtp_auth_pop_before_smtp" },
      { IMAP_SERVER, "imap_server" },
      { IMAP_PORT, "imap_port" },
      { IMAP_USE_SSL, "imap_use_ssl" },
      { IMAP_USE_SPA, "imap_use_spa" },
      { IMAP_USERNAME, "imap_username" },
      { IMAP_FOLDER_PATH, "imap_folder_path" },
      { UNKNOWN, NULL }
    };
    return GetFaultLinkWithMap(fault_code, faultmap);
  }

 private:
  QString GetImapProbeLink(int fault_code) {
    const struct faultmap faultmap[] = {
      { ImapProbe::OK, "ok" },
      { ImapProbe::RESOLVE_FAILED, "resolve_failed" },
      { ImapProbe::CONNECTION_REFUSED, "connection_refused" },
      { ImapProbe::CONNECT_TIMED_OUT, "connect_timed_out" },
      { ImapProbe::SERVER_CLOSED_PREMATURELY, "server_closed_prematurely" },
      { ImapProbe::SOCKET_ERROR, "socket_error" },
      { ImapProbe::IMAP_SERVER_CAFILE_NOT_FOUND,
        "imap_server_cafile_not_found" },
      { ImapProbe::IMAP_SERVER_CERT_SELFSIGNED, "imap_server_cert_selfsigned" },
      { ImapProbe::IMAP_SERVER_CERT_EXPIRED, "imap_server_cert_expired" },
      { ImapProbe::IMAP_SERVER_CERT_NOTYETVALID,
        "imap_server_cert_notyetvalid" },
      { ImapProbe::IMAP_SERVER_CERT_CANNOT_VALIDATE,
        "imap_server_cert_cannot_validate" },
      { ImapProbe::IMAP_SERVER_CERT_NAME_MISMATCH,
        "imap_server_cert_name_mismatch" },
      { ImapProbe::IMAP_SERVER_SHEDDING_LOAD, "imap_server_shedding_load" },
      { ImapProbe::IMAP_SERVER_RESPONSE_MALFORMED,
        "imap_server_response_malformed" },
      { ImapProbe::IMAP_RESPONSE_TIMED_OUT, "imap_response_timed_out" },
      { ImapProbe::IMAP_LOGIN_REJECTED, "imap_login_rejected" },
      { ImapProbe::IMAP_LOGOUT_REJECTED, "imap_logout_rejected" },
      { ImapProbe::IMAP_LOGIN_FAILED_TEMP_SYSTEM_FAILURE,
        "imap_login_failed_temp_system_failure" },
      { ImapProbe::IMAP_NOT_ENABLED_FOR_USER, "imap_not_enabled_for_user" },
      { ImapProbe::IMAP_NOT_ENABLED_FOR_DOMAIN, "imap_not_enabled_for_domain" },
      { ImapProbe::IMAP_NOT_AVAILABLE_FOR_USER, "imap_not_available_for_user" },
      { ImapProbe::IMAP_WEB_LOGIN_REQUIRED, "imap_web_login_required" },
      { ImapProbe::IMAP_ACCOUNT_NOT_READY, "imap_account_not_ready" },
      { ImapProbe::IMAP_LOGIN_FAILED_ABUSE, "imap_login_failed_abuse" },
      { ImapProbe::IMAP_LOGIN_FAILED_GEO_LOCKOUT,
        "imap_login_failed_geo_lockout" },
      { ImapProbe::IMAP_LOGIN_FAILED_BAD_CREDS, "imap_login_failed_bad_creds" },
      { ImapProbe::IMAP_FETCH_STATS_FAILED, "imap_fetch_stats_failed" },
      { ImapProbe::IMAP_FETCH_CONTENTS_FAILED, "imap_fetch_contents_failed" },
      { ImapProbe::IMAP_SELECT_FAILED, "imap_select_failed" },
      { ImapProbe::UNKNOWN, NULL }
    };
    return GetFaultLinkWithMap(fault_code, faultmap);
  }

  QString GetPopProbeLink(int fault_code) {
    const struct faultmap faultmap[] = {
      { PopProbe::OK, "ok" },
      { PopProbe::RESOLVE_FAILED, "resolve_failed" },
      { PopProbe::CONNECTION_REFUSED, "connection_refused" },
      { PopProbe::CONNECT_TIMED_OUT, "connect_timed_out" },
      { PopProbe::SERVER_CLOSED_PREMATURELY, "server_closed_prematurely" },
      { PopProbe::SOCKET_ERROR, "socket_error" },
      { PopProbe::POP_SERVER_CAFILE_NOT_FOUND, "pop_server_cafile_not_found" },
      { PopProbe::POP_SERVER_CERT_SELFSIGNED, "pop_server_cert_selfsigned" },
      { PopProbe::POP_SERVER_CERT_EXPIRED, "pop_server_cert_expired" },
      { PopProbe::POP_SERVER_CERT_NOTYETVALID, "pop_server_cert_notyetvalid" },
      { PopProbe::POP_SERVER_CERT_CANNOT_VALIDATE,
        "pop_server_cert_cannot_validate" },
      { PopProbe::POP_SERVER_CERT_NAME_MISMATCH,
        "pop_server_cert_name_mismatch" },
      { PopProbe::POP_SERVER_SHEDDING_LOAD, "pop_server_shedding_load" },
      { PopProbe::POP_SERVER_RESPONSE_MALFORMED,
        "pop_server_response_malformed" },
      { PopProbe::POP_RESPONSE_TIMED_OUT, "pop_response_timed_out" },
      { PopProbe::POP_CAPA_USER_MISSING, "pop_capa_user_missing" },
      { PopProbe::POP_USER_REJECTED, "pop_user_rejected" },
      { PopProbe::POP_USER_BACKEND_OFFLINE, "pop_user_backend_offline" },
      { PopProbe::POP_LOGIN_FAILED_TEMP_SYSTEM_FAILURE,
        "pop_login_failed_temp_system_failure" },
      { PopProbe::POP_NOT_ENABLED_FOR_USER, "pop_not_enabled_for_user" },
      { PopProbe::POP_LOGIN_FAILED_PERM_SYSTEM_FAILURE,
        "pop_login_failed_perm_system_failure" },
      { PopProbe::POP_LOGIN_FAILED_ABUSE, "pop_login_failed_abuse" },
      { PopProbe::POP_LOGIN_FAILED_GEO_LOCKOUT,
        "pop_login_failed_geo_lockout" },
      { PopProbe::POP_LOGIN_FAILED_BAD_CREDS, "pop_login_failed_bad_creds" },
      { PopProbe::POP_RETR_FAILED, "pop_retr_failed" },
      { PopProbe::POP_STAT_FAILED, "pop_stat_failed" },
      { PopProbe::POP_UIDL_FAILED, "pop_uidl_failed" },
      { PopProbe::POP_LIST_FAILED, "pop_list_failed" },
      { PopProbe::UNKNOWN, NULL }
    };
    return GetFaultLinkWithMap(fault_code, faultmap);
  }

  QString GetSmtpProbeLink(int fault_code) {
    const struct faultmap faultmap[] = {
      { SmtpProbe::OK, "ok" },
      { SmtpProbe::RESOLVE_FAILED, "resolve_failed" },
      { SmtpProbe::CONNECTION_REFUSED, "connection_refused" },
      { SmtpProbe::CONNECT_TIMED_OUT, "connect_timed_out" },
      { SmtpProbe::SERVER_CLOSED_PREMATURELY, "server_closed_prematurely" },
      { SmtpProbe::SOCKET_ERROR, "socket_error" },
      { SmtpProbe::SMTP_SERVER_SHEDDING_LOAD, "smtp_server_shedding_load" },
      { SmtpProbe::SMTP_BANNER_BAD_RESPCODE, "smtp_banner_bad_respcode" },
      { SmtpProbe::SMTP_SERVER_RESPONSE_MALFORMED,
        "smtp_server_response_malformed" },
      { SmtpProbe::SMTP_SERVER_RESPONSE_TIMED_OUT,
        "smtp_server_response_timed_out" },
      { SmtpProbe::SMTP_SERVER_BAD_RESPCODE, "smtp_server_bad_respcode" },
      { SmtpProbe::SMTP_SERVER_NOT_OFFERING_TLS,
        "smtp_server_not_offering_tls" },
      { SmtpProbe::SMTP_SERVER_NOT_OFFERING_AUTH,
        "smtp_server_not_offering_auth" },
      { SmtpProbe::SMTP_SERVER_CERT_SELFSIGNED, "smtp_server_cert_selfsigned" },
      { SmtpProbe::SMTP_SERVER_CERT_EXPIRED, "smtp_server_cert_expired" },
      { SmtpProbe::SMTP_SERVER_CERT_NOTYETVALID,
        "smtp_server_cert_notyetvalid" },
      { SmtpProbe::SMTP_SERVER_CERT_CANNOTVALIDATE,
        "smtp_server_cert_cannotvalidate" },
      { SmtpProbe::SMTP_SERVER_CERT_NAME_MISMATCH,
        "smtp_server_cert_name_mismatch" },
      { SmtpProbe::SMTP_SERVER_CAFILE_NOT_FOUND,
        "smtp_server_cafile_not_found" },
      { SmtpProbe::SMTP_SERVER_LOGIN_REJECTED, "smtp_server_login_rejected" },
      { SmtpProbe::SMTP_SERVER_LOGIN_BAD_CODE, "smtp_server_login_bad_code" },
      { SmtpProbe::SMTP_SERVER_LOGIN_BACKEND_OFFLINE,
        "smtp_server_login_backend_offline" },
      { SmtpProbe::SMTP_SERVER_REJECTED_MAIL_FROM,
        "smtp_server_rejected_mail_from" },
      { SmtpProbe::SMTP_SERVER_REJECTED_RCPT_TO,
        "smtp_server_rejected_rcpt_to" },
      { SmtpProbe::SMTP_SERVER_FAILED_RSET, "smtp_server_failed_rset" },
      { SmtpProbe::SMTP_SERVER_REJECTED_QUIT, "smtp_server_rejected_quit" },
      { SmtpProbe::UNKNOWN, NULL }
    };
    return GetFaultLinkWithMap(fault_code, faultmap);
  }

 public:
  QString GetProbeLink(QString probe_name, int fault_code) {
    if (probe_name == "imap")
      return GetImapProbeLink(fault_code);
    else if (probe_name == "pop")
      return GetPopProbeLink(fault_code);
    else if (probe_name == "smtp")
      return GetSmtpProbeLink(fault_code);
    else
      return GetSupportLink(Support::UNKNOWN);
  }
};

Provider *InitProvider() {
  return new GmailProvider();
}

}  // namespace popdiag
